//
// Created by benxb on 2021/12/24.
//

#include <gtest/gtest.h>
#include <common/logger.h>
#include <string>
#include <index/linear_index_segment_reader.h>

/**
 * Test simple append index
 * append some index and retrieve from segment
 */
TEST(IndexSegmentTest, AppendIndex) {
    std::string path = "/home/ubuntu/benxdb/data";
    benxdb::LinearIndexSegment segment(path, 1, benxdb::STYPE_FLOAT);
    float key1 = 1.231;
    float key2 = 3.231;
    float key3 = 9.231;
    benxdb::LinearIndexLeafPair pair1(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key1), 0);
    benxdb::LinearIndexLeafPair pair2(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key2), 1);
    benxdb::LinearIndexLeafPair pair3(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key3), 2);
    segment.AppendIndexPair(pair1);
    segment.AppendIndexPair(pair2);
    segment.AppendIndexPair(pair3);
    benxdb::LinearIndexPage page;
    segment.GetIndexPage(0, page);
    auto gpair1 = page.GetIndexPair(0);
    auto gpair2 = page.GetIndexPair(1);
    auto gpair3 = page.GetIndexPair(2);
    GTEST_ASSERT_EQ(gpair1.key.GetAs<float>(), pair1.key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair2.key.GetAs<float>(), pair2.key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair3.key.GetAs<float>(), pair3.key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), 3);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), (benxdb::PAGE_SIZE - page.GetHeaderSize())/12-3);
}


/**
 * Test append many index
 */
TEST(IndexSegmentTest, AppendManyIndex) {
    std::string path = "/home/ubuntu/benxdb/data";
    benxdb::LinearIndexSegment segment(path, 1, benxdb::STYPE_FLOAT);
    float key1 = 1.231;
    float key2 = 3.231;
    float key3 = 9.231;
    float key4 = 4.231;
    float key5 = 5.231;
    benxdb::LinearIndexLeafPair pair1(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key1), 0);
    benxdb::LinearIndexLeafPair pair2(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key2), 1);
    benxdb::LinearIndexLeafPair pair3(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key3), 2);
    benxdb::LinearIndexLeafPair pair4(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key4), 3);
    benxdb::LinearIndexLeafPair pair5(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key5), 4);
    std::vector<benxdb::LinearIndexLeafPair> pairs;
    pairs.emplace_back(pair1);
    pairs.emplace_back(pair2);
    pairs.emplace_back(pair3);
    pairs.emplace_back(pair4);
    pairs.emplace_back(pair5);
    segment.AppendManyIndexPairs(pairs, 1, 3);
    benxdb::LinearIndexPage page;
    segment.GetIndexPage(0, page);
    auto gpair1 = page.GetIndexPair(0);
    auto gpair2 = page.GetIndexPair(1);
    auto gpair3 = page.GetIndexPair(2);
    GTEST_ASSERT_EQ(gpair1.key.GetAs<float>(), pair2.key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair2.key.GetAs<float>(), pair3.key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair3.key.GetAs<float>(), pair4.key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), 3);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), (benxdb::PAGE_SIZE - page.GetHeaderSize())/12-3);
}

/**
 * Test append one page index
 */
TEST(IndexSegmentTest, AppendOnePageIndex) {
    std::string path = "/home/ubuntu/benxdb/data";
    benxdb::LinearIndexSegment segment(path, 1, benxdb::STYPE_FLOAT);
    benxdb::LinearIndexPage page(benxdb::STYPE_FLOAT);
    int page_count = page.GetFreeSlotSize();
    std::vector<benxdb::LinearIndexLeafPair> pairs;
    for (int i = 0; i < page_count+1; i++) {
        float key = i;
        benxdb::LinearIndexLeafPair pair(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key), i);
        pairs.push_back(pair);
        segment.AppendIndexPair(pair);
    }
    GTEST_ASSERT_EQ(segment.GetPageCount(), 1);
    segment.GetIndexPage(0, page);
    auto gpair1 = page.GetIndexPair(0);
    auto gpair2 = page.GetIndexPair(1);
    auto gpair3 = page.GetIndexPair(2);
    GTEST_ASSERT_EQ(gpair1.key.GetAs<float>(), pairs[0].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair2.key.GetAs<float>(), pairs[1].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair3.key.GetAs<float>(), pairs[2].key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), page_count);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), 0);

    segment.GetIndexPage(1, page);
    auto gpair4 = page.GetIndexPair(0);
    GTEST_ASSERT_EQ(gpair4.key.GetAs<float>(), pairs.back().key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), 1);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), page_count-1);
}

/**
 * Test append many page index
 */
TEST(IndexSegmentTest, AppendManyPageIndex) {
    std::string path = "/home/ubuntu/benxdb/data";
    benxdb::LinearIndexSegment segment(path, 1, benxdb::STYPE_FLOAT);
    benxdb::LinearIndexPage page(benxdb::STYPE_FLOAT);
    int page_count = page.GetFreeSlotSize();
    std::vector<benxdb::LinearIndexLeafPair> pairs;
    for (int i = 0; i < page_count*3+1; i++) {
        float key = i;
        benxdb::LinearIndexLeafPair pair(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key), i);
        pairs.push_back(pair);
        segment.AppendIndexPair(pair);
    }
    GTEST_ASSERT_EQ(segment.GetPageCount(), 3);
    segment.GetIndexPage(0, page);
    auto gpair1 = page.GetIndexPair(0);
    auto gpair2 = page.GetIndexPair(1);
    auto gpair3 = page.GetIndexPair(2);
    GTEST_ASSERT_EQ(gpair1.key.GetAs<float>(), pairs[0].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair2.key.GetAs<float>(), pairs[1].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair3.key.GetAs<float>(), pairs[2].key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), page_count);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), 0);

    segment.GetIndexPage(1, page);
    gpair1 = page.GetIndexPair(0);
    gpair2 = page.GetIndexPair(1);
    gpair3 = page.GetIndexPair(2);
    GTEST_ASSERT_EQ(gpair1.key.GetAs<float>(), pairs[page_count].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair2.key.GetAs<float>(), pairs[page_count+1].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair3.key.GetAs<float>(), pairs[page_count+2].key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), page_count);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), 0);

    segment.GetIndexPage(2, page);
    gpair1 = page.GetIndexPair(0);
    gpair2 = page.GetIndexPair(1);
    gpair3 = page.GetIndexPair(2);
    GTEST_ASSERT_EQ(gpair1.key.GetAs<float>(), pairs[page_count*2].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair2.key.GetAs<float>(), pairs[page_count*2+1].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair3.key.GetAs<float>(), pairs[page_count*2+2].key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), page_count);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), 0);

    segment.GetIndexPage(3, page);
    auto gpair4 = page.GetIndexPair(0);
    GTEST_ASSERT_EQ(gpair4.key.GetAs<float>(), pairs.back().key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), 1);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), page_count-1);
}

/**
 * Test get max min key
 */
TEST(IndexSegmentTest, TestMinMaxKey) {
    std::string path = "/home/ubuntu/benxdb/data";
    benxdb::LinearIndexSegment segment(path, 1, benxdb::STYPE_FLOAT);
    benxdb::LinearIndexPage page(benxdb::STYPE_FLOAT);
    int page_count = page.GetFreeSlotSize();
    std::vector<benxdb::LinearIndexLeafPair> pairs;
    for (int i = 0; i < page_count*3+1; i++) {
        float key = i;
        benxdb::LinearIndexLeafPair pair(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key), i);
        pairs.push_back(pair);
        segment.AppendIndexPair(pair);
    }
    GTEST_ASSERT_EQ(0, segment.GetMinKey().GetAs<float>());
    GTEST_ASSERT_EQ(page_count*3, segment.GetMaxKey().GetAs<float>());
    benxdb::LinearIndexKey mid_key = segment.GetApproximateMid();
    GTEST_ASSERT_EQ((page_count*3)*1.0/2, mid_key.GetAs<float>());
    GTEST_ASSERT_EQ(3, segment.GetPageCount());
}

/**
 * Test create temp and rename segment
 */
TEST(IndexSegmentTest, TestCreateTempSegment) {
    std::string path = "/home/ubuntu/benxdb/data";
    benxdb::LinearIndexSegment segment(path, 1, benxdb::STYPE_FLOAT);
    benxdb::LinearIndexPage page(benxdb::STYPE_FLOAT);
    int page_count = page.GetFreeSlotSize();
    std::vector<benxdb::LinearIndexLeafPair> pairs;
    for (int i = 0; i < page_count*3+1; i++) {
        float key = i;
        benxdb::LinearIndexLeafPair pair(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key), i);
        pairs.push_back(pair);
        segment.AppendIndexPair(pair);
    }
    std::shared_ptr<benxdb::LinearIndexSegment> nseg = segment.CreateTempSegment(segment.GetSegmentId());
    for (int i = 0; i < page_count*3+1; i++) {
        float key = i;
        benxdb::LinearIndexLeafPair pair(benxdb::STYPE_FLOAT, reinterpret_cast<const char *>(&key), i);
        pairs.push_back(pair);
        nseg->AppendIndexPair(pair);
    }
    nseg->GetIndexPage(0, page);
    auto gpair1 = page.GetIndexPair(0);
    auto gpair2 = page.GetIndexPair(1);
    auto gpair3 = page.GetIndexPair(2);
    GTEST_ASSERT_EQ(gpair1.key.GetAs<float>(), pairs[0].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair2.key.GetAs<float>(), pairs[1].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair3.key.GetAs<float>(), pairs[2].key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), page_count);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), 0);

    nseg->GetIndexPage(1, page);
    gpair1 = page.GetIndexPair(0);
    gpair2 = page.GetIndexPair(1);
    gpair3 = page.GetIndexPair(2);
    GTEST_ASSERT_EQ(gpair1.key.GetAs<float>(), pairs[page_count].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair2.key.GetAs<float>(), pairs[page_count+1].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair3.key.GetAs<float>(), pairs[page_count+2].key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), page_count);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), 0);

    nseg->GetIndexPage(2, page);
    gpair1 = page.GetIndexPair(0);
    gpair2 = page.GetIndexPair(1);
    gpair3 = page.GetIndexPair(2);
    GTEST_ASSERT_EQ(gpair1.key.GetAs<float>(), pairs[page_count*2].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair2.key.GetAs<float>(), pairs[page_count*2+1].key.GetAs<float>());
    GTEST_ASSERT_EQ(gpair3.key.GetAs<float>(), pairs[page_count*2+2].key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), page_count);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), 0);

    nseg->GetIndexPage(3, page);
    auto gpair4 = page.GetIndexPair(0);
    GTEST_ASSERT_EQ(gpair4.key.GetAs<float>(), pairs.back().key.GetAs<float>());
    GTEST_ASSERT_EQ(page.GetKeyType(), benxdb::STYPE_FLOAT);
    GTEST_ASSERT_EQ(page.GetIndexSize(), 1);
    GTEST_ASSERT_EQ(page.GetFreeSlotSize(), page_count-1);

    nseg->RenameSegment(2);
    GTEST_ASSERT_EQ(nseg->GetSegmentId(), 2);
}